
最近終於完成公司新產品的前端開發啦!不過在開發時也遇到了不少挑戰,其中最難處理的就屬「MQTT通訊協定」的部分了...😅
而自己在study或是Google資料的過程中,發現「React.js + MQTT」的資訊好像比較少一些,因此也在這邊把自己的處理作法紀錄下來,之後若有更佳的作法也會再更新👍
MQTT是一種輕量級的通訊協定,目前主要在物聯網(IoT)領域中被廣泛使用。而其採用「Publish發佈/Subscribe訂閱」的模式,當發佈者將訊息發佈在一個Topic(主題)後,即透過Broker(訊息代理)轉發,讓所有已訂閱該Topic的訂閱者接收到訊息。
以上關於協定的簡介就先到這邊,更詳細的概念說明可以參考這一篇囉 ↓
https://resource.webduino.io/blog/mqtt-guide
一般情況是直接安裝官方的「MQTT.js」,但在React.js的專案裡使用,可能會產生不少Webpack環境以及執行上的錯誤。不過幸運的是,近期已有熱心人士提供修正完成的版本,因此若想節省時間、直接使用的話,建議可以安裝「precompiled-mqtt」。
npm i precompiled-mqtt
也提供MQTT.js官方文件於下方連結,內容中有各API的使用說明:
https://github.com/mqttjs/MQTT.js
由於主要為了測試MQTT的收發,就先用幾個簡單元件來建立demo頁面吧:
import { useEffect, useState } from 'react';
const MqttDemo = () => {
     const [data, setData] = useState(
         {
             topic: '-',
             message: '-',
         }
     );
     return (
         <div style={{width: '400px', margin: 'auto'}}>
             <h3>
               {`topic: ${data.topic}, Message: ${data.message}`}
             </h3>
             <button type='button'>
                 {'發佈(Publish)'}
             </button>
         </div>
     )
};
export default MqttDemo;

import mqtt from 'precompiled-mqtt';
在沒有架設Broker的情況下,可以使用「Eclipse Mosquitto™」所提供的測試Server來實作收發功能( https://test.mosquitto.org/ )。
★ 不過要注意的是,由於瀏覽器(Browser)環境不支援mqtt通訊協定,因此Web應用程式須透過WebSocket協定來連線。將瀏覽器成為基於WebSocket的MQTT client(MQTT over WebScoket),才能直接接收資料。
找到Port 8081就是我們需要的「MQTT over WebScoket」後,使用connect API連線、即可取得一個Client EventEmitter(事件發射器)。
const client = mqtt.connect('https://test.mosquitto.org:8081/');
取得Client後,就可以透過「subscribe API」與「監聽message事件」,來訂閱指定主題與接收訊息。
在message事件的callback參數中,「topic」代表這則訊息是由哪個主題發送過來的,而「payload」則是其訊息內容。
而由於payload的資料型態是「Buffer」,所以需要使用toString()將其轉換為字串。
const subMessage = (client) => {
     client.subscribe('/test1103');
     client.on('message', (topic, payload) => {
         const data2String = payload.toString();
         setData(
             {
                 topic: topic, 
                 message: JSON.parse(data2String)
             }
         );    
     });
};
最後將上方寫好function傳入useEffect。讓React在render完成後,直接開始訂閱與接收訊息,並在元件卸載時與Broker結束連線(若沒有結束連線,即便切換路徑也會持續收到訊息)。
e.g. 現在當有人把”Hello”訊息發佈到「test1103」這個主題,這邊的message事件就會觸發、並接收到”Hello”。
基本的MQTT連線/訂閱/接收,在這個階段就算是實作完成了。不過若沒有訊息發佈的話,其實也不易確認是否有正常運作吧~接著就直接實作一個發佈(Publish)功能來驗證看看囉!
useEffect(()=>{
     subMessage(client);
     return () => {
         client.end();
     };
},[])
我們將發佈功能寫成function,並綁定在按鈕的onClick事件裡,每次點擊就發佈一則訊息出去。
而發佈的topic就是我們已訂閱好的「test1103」、message內容則設定為發佈當下的時戳,以便於確認每次發佈的訊息是否皆有收到。
★ message的資料型態必須是「字串」、「Buffer」或「ArrayBuffer」,因此將時戳轉換為字串後再發佈。
const pubMessage = (client) => {
     const getTimestamp = new Date().getTime();
     client.publish('/test1103', `${getTimestamp}`);
};
<button 
     type='button'
     onClick={()=>{pubMessage(client)}}
     >
     {'發佈(Publish)'}
</button>
最後就可以來實際點擊測試啦!從下圖可以看到,我們每次發佈與接收到的「主題+訊息」。

import mqtt from 'precompiled-mqtt';
import { useEffect, useState } from 'react';
const MqttDemo = () => {
    const client = mqtt.connect('https://test.mosquitto.org:8081/');
    const [data, setData] = useState(
        {
            topic: '-',
            message: '-',
        }
    );
    const subMessage = (client) => {
        client.subscribe('/test1103');
        client.on('message', (topic, payload) => {
            const data2String = payload.toString();
            setData(
                {
                    topic: topic, 
                    message: JSON.parse(data2String)
                }
            );    
        });
    };
    const pubMessage = (client) => {
        const getTimestamp = new Date().getTime();
        client.publish('/test1103', `${getTimestamp}`);
    };
    useEffect(()=>{
        subMessage(client);
        return () => {
            client.end();
        };
    },[])
    return (
        <div style={{width: '400px', margin: 'auto'}}>
            <h3>
                {`topic: ${data.topic}, Message: ${data.message}`}
            </h3>
            <button 
                type='button'
                onClick={()=>{pubMessage(client)}}
                >
                {'發佈(Publish)'}
            </button>
        </div>
    )
};
export default MqttDemo;
以上React+MQTT的基本使用差不多就到這邊啦~
不過之前開發過程中,最頭痛的是在搭配Highcharts作時數圖的部分,後續再把相關過程整理上來囉!